/*
 * Copyright 2008 the Breachwalls OSS project and the Others.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package com.breachwalls.quasar.datatype.generic;

import java.util.ArrayList;
import java.util.List;

import com.breachwalls.quasar.messages.ErrorMessage;
import com.breachwalls.quasar.messages.ErrorMessageArrayList;
import com.breachwalls.quasar.messages.ErrorMessageList;
import com.breachwalls.quasar.datatype.Datatype;
import com.breachwalls.quasar.datatype.validator.DatatypeValidator;
import com.breachwalls.quasar.datatype.validator.DatatypeValidatorFactory;
import com.breachwalls.quasar.datatype.validator.ValidatorConfig;

import org.apache.commons.lang.builder.CompareToBuilder;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;

/**
 * 汎用データタイプ.
 * @author todoken
 * @version $Revision: 128 $
 */
public class GenericDatatype implements Datatype, Cloneable, Comparable<Datatype> {

    /**
     * シリアルバージョンID.
     */
    private static final long serialVersionUID = 1L;

    /**
     * バリデータの設定値.
     */
    private List<ValidatorConfig> validatorConfigs = new ArrayList<ValidatorConfig>();

    /**
     * エラーメッセージ.
     */
    private ErrorMessageList messages = new ErrorMessageArrayList();

    /**
     * データタイプ内部で保持する文字列.
     */
    private String value;

    /**
     * コンストラクタ.
     */
    public GenericDatatype() {
        initialize();
    }

    /**
     * コンストラクタ.
     * @param newValue データタイプ内部で保持する文字列
     */
    public GenericDatatype(final String newValue) {
        this();
        this.value = newValue;
    }

    /**
     * このデータタイプを初期化する.
     */
    protected void initialize() {
    }

    /**
     * データタイプ内部で保持している文字列を取得する.
     * @return データタイプ内部で保持している文字列
     */
    protected String getValue() {
        return value;
    }

    /**
     * データタイプ内部で保持する文字列を設定する.
     * @param newValue データタイプ内部で保持する文字列
     */
    protected void setValue(final String newValue) {
        this.value = newValue;
    }

    /**
     * データタイプにバリデータの設定値を追加する.<br>
     * この設定値はvalidateメソッドを呼び出したときに使用されます.
     * @param config 追加するバリデータの設定値
     * @see Datatype#validate()
     * @see Datatype#validate(String);
     */
    protected void addValidatorConfig(final ValidatorConfig config) {
        this.validatorConfigs.add(config);
    }

    /**
     * {@inheritDoc}
     */
    public boolean isNull() {
        return this.value == null;
    }

    /**
     * {@inheritDoc}
     */
    public int length() {
        if (isNull()) {
            return 0;
        }
        return this.value.length();
    }

    /**
     * {@inheritDoc}
     */
    public boolean hasErrors() {
        return messages.hasErrors();
    }

    /**
     * {@inheritDoc}
     */
    public ErrorMessageList getErrorMessages() {
        return messages;
    }

    /**
     * {@inheritDoc}
     */
    public void addErrorMessage(final ErrorMessage error) {
        if (error != null) {
            messages.add(error);
        }
    }

    /**
     * {@inheritDoc}
     */
    public boolean validate() {
        return validate(null);
    }

    /**
     * {@inheritDoc}
     */
    public boolean validate(final String fieldName) {
        for (ValidatorConfig config : this.validatorConfigs) {
            final String n = config.getName();
            DatatypeValidator validator = DatatypeValidatorFactory.getDatatypeValidator(n);
            validator.validate(fieldName, this, config);
        }
        return !hasErrors();
    }

    /**
     * {@inheritDoc}
     */
    public int compareTo(final Datatype datatype) {
        if (datatype != null && datatype instanceof GenericDatatype) {
            GenericDatatype target = (GenericDatatype) datatype;
            if (!target.isNull()) {
                return target.getValue().compareTo(getValue());
            }
        }
        return CompareToBuilder.reflectionCompare(this, datatype);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(final Object o) {
        return EqualsBuilder.reflectionEquals(this, o);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int hashCode() {
        final int a = 12;
        final int b = 36;
        HashCodeBuilder builder = new HashCodeBuilder(a, b);
        builder.append(this.value);
        return builder.hashCode();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        return this.value;
    }

}
